package com.misty.java8;

import org.junit.Test;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
import java.util.Locale;
import java.util.TimeZone;

public class DateAndTime {
    /**
     * 现有Api存在的问题
     *     线程安全: Date和Calendar不是线程安全的，你需要编写额外的代码处理线程安全问题
     *     DateFormat方法也有它自己的问题。比如，它不是线程安全的 这意味着两个线程如果尝试使用同一个formatter解析日期，你可能会得到无法预期的结果。
     *     API设计和易用性: 由于Date和Calendar的设计不当你无法完成日常的日期操作
     *     * 2014年3月18日 `Date date = new Date(114, 2, 18);`
     *     ZonedDate和Time: 你必须编写额外的逻辑处理时区和那些旧的逻辑
     */

    //LocalDate LocalTime Instant Duration以及Period

    //LocalDate:该类的实例是一个不可变对象，它只提供了简单的日期，并不含当天的时间信息。另外，它也不附带任何与时区相关的信息。
    @Test
    public void test1(){
        LocalDate date = LocalDate.of(2014, 3, 18); //2014-03-18
        int year = date.getYear();    //2014
        Month month = date.getMonth();   //MARCH
        int day = date.getDayOfMonth();    //18
        DayOfWeek dow = date.getDayOfWeek();   //TUESDAY
        int len = date.lengthOfMonth();   //31 (days in March)
        boolean leap = date.isLeapYear(); //false (not a leap year)
        LocalDate today = LocalDate.now();//工厂方法从系统时钟中获取当前的日期
    }

   // 使用TemporalField读取LocalDate的值
    @Test
    public void test2(){
        LocalDate date = LocalDate.of(2014, 3, 18); //2014-03-18
        int year = date.get(ChronoField.YEAR);
        int month = date.get(ChronoField.MONTH_OF_YEAR);
        int day = date.get(ChronoField.DAY_OF_MONTH);
    }

    //LocalDate和LocalTime都可以通过解析代表它们的字符串创建。使用静态方法parse，你可以实现这一目的：
    @Test
    public void test3(){
        LocalDate date = LocalDate.parse("2014-03-18");
        LocalTime time = LocalTime.parse("13:45:20");
        //你可以向parse方法传递一个DateTimeFormatter。该类的实例定义了如何格式化一个日期或者时间对象。正如我们之前所介绍的，它是替换老版java.util.DateFormat的推荐替代品
        // 一旦字符串不合法则会抛出一个继承自RuntimeException的DateTimeParseException异常
        LocalDateTime.now().format(DateTimeFormatter.ofPattern( "yyyy-MM-dd HH:mm:ss"));
    }



    //直接创建LocalDateTime对象，或者通过合并日期和时间的方式创建
    @Test
    public void test4(){
        LocalDate date = LocalDate.parse("2014-03-18");
        LocalTime time = LocalTime.parse("13:45:20");
        LocalDate date2 = LocalDate.of(2014, 3, 18); //2014-03-18
        LocalDateTime dt1 = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45, 20);
        LocalDateTime dt2 = LocalDateTime.of(date, time);
        LocalDateTime dt3 = date.atTime(13, 45, 20);
        LocalDateTime dt4 = date.atTime(time);
        LocalDateTime dt5 = time.atDate(date);
        //也可以使用toLocalDate或者toLocalTime方法，从LocalDateTime中提取LocalDate或者LocalTime组件
        LocalDate date1 = dt1.toLocalDate();    //2014-03-18
        LocalTime time1 = dt1.toLocalTime();    //13:45:20
    }

    //Instant类也支持静态工厂方法now，它能够帮你获取当前时刻的时间戳。   Instant的设计初衷是为了便于机器使用。它包含的是由秒及纳秒所构成的数字
    @Test
    public void test5(){
        //由于LocalDateTime和Instant是为不同的目的而设计的，一个是为了便于人阅读使用，另一个是为了便于机器处理，
        // 所以你不能将二者混用。如果你试图在这两类对象之间创建duration，会触发一个DateTimeException异常.UnsupportedTemporalTypeException
        int day = Instant.now().get(ChronoField.DAY_OF_MONTH);
        LocalDateTime localDateTime1 = LocalDateTime.now();
        LocalDateTime localDateTime2 = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45, 20);
        LocalDate time1 = localDateTime1.toLocalDate();
        LocalDate time2 = localDateTime1.toLocalDate();
        //用于处理机器时间　Duration
        Duration d1 = Duration.between(time1, time2);
        Duration d3 = Duration.between(localDateTime1, localDateTime2);

        Instant.ofEpochSecond(3);
        Instant.ofEpochSecond(3, 0);
        //2 秒之后再加上100万纳秒（1秒）
        Instant instant1 = Instant.ofEpochSecond(2, 1_000_000_000);
        Instant instant2  = Instant.ofEpochSecond(4, -1_000_000_000);

        Duration d2 = Duration.between(instant1, instant2);

        //用于处理自然时间Period
        Period tenDays = Period.between(LocalDate.of(2014, 3, 8),
                LocalDate.of(2014, 3, 18));
    }

    @Test
    public void test6(){
        Duration threeMinutes = Duration.ofMinutes(3);
        Duration threeMinutes2 = Duration.of(3, ChronoUnit.MINUTES);

        Period tenDays = Period.ofDays(10);
        Period threeWeeks = Period.ofWeeks(3);
        Period twoYearsSixMonthsOneDay = Period.of(2, 6, 1);
    }

    // 直观的方式操纵LocalDate的属性
    @Test
    public void test7(){
        LocalDate date1 = LocalDate.of(2014, 3, 18);//2014-03-18
        LocalDate date2 = date1.withYear(2011);    //2011-03-18
        LocalDate date3 = date2.withDayOfMonth(25);    //2011-03-25
        LocalDate date4 = date3.with(ChronoField.MONTH_OF_YEAR, 9);    //2011-09-25
    }

    //以相对方式修改LocalDate对象的属性
    @Test
    public void test8(){
        LocalDate date1 = LocalDate.of(2014, 3, 18);    //2014-03-18
        LocalDate date2 = date1.plusWeeks(1);    //2014-03-25
        LocalDate date3 = date2.minusYears(3);    //2011-03-25
        LocalDate date4 = date3.plus(6, ChronoUnit.MONTHS);    //2011-09-25
    }

    //一段时间处理的综合应用
    @Test
    public void test9(){
        LocalDate date = LocalDate.of(2014, 3, 18);
        date = date.with(ChronoField.MONTH_OF_YEAR, 9);
        date = date.plusYears(2).minusDays(10);
        date.withYear(2011);
    }

    //使用TemporalAdjuster(todo)

    //按照某个模式创建DateTimeFormatter
    @Test
    public void test10(){
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
        LocalDate date1 = LocalDate.of(2014, 3, 18);
        String formattedDate = date1.format(formatter);
        LocalDate date2 = LocalDate.parse(formattedDate, formatter);
    }

    //创建一个本地化的DateTimeFormatter
    @Test
    public void test11(){
        DateTimeFormatter italianFormatter =
                DateTimeFormatter.ofPattern("d. MMMM yyyy", Locale.ITALIAN);
        LocalDate date1 = LocalDate.of(2014, 3, 18);
        String formattedDate = date1.format(italianFormatter); // 18. marzo 2014
        LocalDate date2 = LocalDate.parse(formattedDate, italianFormatter);
    }

    //DateTimeFormatterBuilder类还提供了更复杂的格式器，你可以选择恰当的方法，一步一步地构造自己的格式器。另外，它还提供了非常强大的解析功能，
    // 比如区分大小写的解析、柔性解析（允许解析器使用启发式的机制去解析输入，不精确地匹配指定的模式）、填充，以及在格式器中指定可选节
    @Test
    public void test12(){
        DateTimeFormatter italianFormatter = new DateTimeFormatterBuilder()
                .appendText(ChronoField.DAY_OF_MONTH)
                .appendLiteral(". ")
                .appendText(ChronoField.MONTH_OF_YEAR)
                .appendLiteral(" ")
                .appendText(ChronoField.YEAR)
                .parseCaseInsensitive()
                .toFormatter(Locale.ITALIAN);
    }

    @Test
    public void test13(){
        //时区是按照一定的规则将区域划分成的标准时间相同的区间。在ZoneRules这个类中包含了40个这样的实例。你可以简单地通过调用ZoneId的getRules()得到指定时区的规则
        ZoneId romeZone = ZoneId.of("Europe/Rome");
        //通过Java 8的新方法toZoneId将一个老的时区对象转换为ZoneId
        ZoneId zoneId = TimeZone.getDefault().toZoneId();

        //通过ZoneId，你还可以将LocalDateTime转换为Instant：
        LocalDateTime dateTime = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45);
        Instant instantFromDateTime =  dateTime.atZone(ZoneId.of("Europe/Paris")).toInstant();

        //你也可以通过反向的方式得到LocalDateTime对象：
        Instant instant = Instant.now();
        LocalDateTime timeFromInstant = LocalDateTime.ofInstant(instant, romeZone);
    }
}
